/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2015-2018. All rights reserved.
 * Description: Signature Verify CBB Library
 * Author: tangzonglei
 * Create: 2015
 * History: 2018/11/23 yebin code rule fixes
 */
#include "../x509/cmscbb_x509.h"
#include "cmscbb_sdk.h"
#include "../asn1/cmscbb_asn1_decode.h"
#include "../asn1/cmscbb_asn1_utils.h"
#include "../cmscbb_common/cmscbb_common.h"
#include "../cmscbb_common/cmscbb_def.h"
#include "../x509/cmscbb_verify_info.h"

#if CMSCBB_WITHOUT_SECUREC == 0
#include "securec.h"
#endif

CMSCBB_ERROR_CODE CmscbbX509IsSelfSigned(CmscbbX509Cert *cert, CVB_BOOL *isSelfSigned)
{
    CMSCBB_ERROR_CODE ret;
    CVB_BOOL verifyResult = CVB_FALSE;

    if (cert == CVB_NULL || isSelfSigned == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    *isSelfSigned = CVB_FALSE;
    /* check if the certificate already verified */
    if (cert->issuer == cert) {
        *isSelfSigned = CVB_TRUE;
        return CVB_SUCCESS;
    }

    /* name check */
    if (CmscbbCompareX509Name(&(cert->toBeSigned.subject), &(cert->toBeSigned.issuer)) != 0) {
        return CVB_SUCCESS;
    }

    ret = CmscbbX509PubKeyVerify(&(cert->rawSigned), &(cert->signature),
        &cert->toBeSigned.subjectPubKey, &(cert->algorithm), &verifyResult);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbCheckAlgoPara(&(cert->toBeSigned.subjectPubKey.algorithm), &(cert->algorithm));
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    /* set the issuer if verify passed */
    if (verifyResult == CVB_TRUE) {
        *isSelfSigned = CVB_TRUE;
        cert->issuer = cert;
    } else {
        *isSelfSigned = CVB_FALSE;
    }
    return CVB_SUCCESS;
}

#ifdef CVB_DEBUG
static CVB_VOID InternalExtractCertName(CmscbbX509Cert *dest)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 nameLen = 0;

    if (dest == CVB_NULL) {
        return;
    }

    ret = CmscbbConvertFromX509Name(&(dest->toBeSigned.issuer), &(dest->toBeSigned.issuerName), &nameLen);
    if (CVB_FAILED(ret)) {
        CVB_LOG_WARNING(ret, CVB_NULL);
    }
    ret = CmscbbConvertFromX509Name(&(dest->toBeSigned.subject), &(dest->toBeSigned.subjectName), &nameLen);
    if (CVB_FAILED(ret)) {
        CVB_LOG_WARNING(ret, CVB_NULL);
    }
}
#endif /* CVB_DEBUG */

CMSCBB_ERROR_CODE CmscbbX509DecodeCert(const CVB_BYTE *encodedCert, CVB_UINT32 encodedLength,
    CmscbbX509Cert **cert, CVB_UINT32 *bytesDecoded)
{
    CmscbbX509Cert *dest = CVB_NULL;
    CMSCBB_ERROR_CODE ret;

    if ((encodedCert == CVB_NULL) || (encodedLength == 0) || (cert == CVB_NULL) || (bytesDecoded == CVB_NULL)) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbAsnDecode(encodedCert, encodedLength, CmscbbGetCmscbbX509Cert(), CVB_ASN_NORMAL,
        (CVB_VOID **)(&dest), bytesDecoded);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

#ifdef CVB_DEBUG
    InternalExtractCertName(dest);
#endif /* CVB_DEBUG */

    dest->oriCertAddr = encodedCert;
    dest->certLength = *bytesDecoded;
    dest->iRef = 1;
    *cert = dest;

    return CVB_SUCCESS;
}

CVB_VOID CmscbbX509FreeCert(CmscbbX509Cert *cert)
{
    if (cert == CVB_NULL) {
        return;
    }

    --cert->iRef;
    if (cert->iRef <= 0) {
#ifdef CVB_DEBUG
        CmscbbFree(cert->toBeSigned.issuerName);
        CmscbbFree(cert->toBeSigned.subjectName);
#endif /* CVB_DEBUG */

        CmscbbFree(cert);
    }
}

CMSCBB_ERROR_CODE CmscbbX509DecodeCrl(const CVB_BYTE *encodedCrl, CVB_UINT32 encodedLen, CmscbbX509Crl **crl,
    CVB_UINT32 *bytesDecoded)
{
    CmscbbX509Crl *dest = CVB_NULL;
    CMSCBB_ERROR_CODE ret;

    if ((encodedCrl == CVB_NULL) || (encodedLen == 0) || (crl == CVB_NULL) || (bytesDecoded == CVB_NULL)) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbAsnDecode(encodedCrl, encodedLen, CmscbbGetCmscbbX509Crl(), CVB_ASN_NORMAL,
        (CVB_VOID **)(&dest), bytesDecoded);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

#ifdef CVB_DEBUG
    CVB_UINT32 nameLen = 0;
    (CVB_VOID)CmscbbConvertFromX509Name(&(dest->tbsCertList.issuer), &(dest->tbsCertList.issuerName), &nameLen);
#endif /* CVB_DEBUG */
    dest->oriCrlAddr = encodedCrl;
    dest->crlLength = *bytesDecoded;
    dest->iRef = 1;
    *crl = dest;
    return CVB_SUCCESS;
}

CVB_VOID CmscbbX509FreeCrl(CmscbbX509Crl *crl)
{
    if (crl == CVB_NULL) {
        return;
    }

    --crl->iRef;
    if (crl->iRef <= 0) {
#ifdef CVB_DEBUG
        CmscbbFree(crl->tbsCertList.issuerName);
#endif
        CmscbbFree(crl);
    }
}

CMSCBB_ERROR_CODE CmscbbCompareX509Name(const CmscbbX509Name *nameExpect, const CmscbbX509Name *nameActual)
{
    CVB_INT iter;

    if (nameExpect == CVB_NULL || nameActual == CVB_NULL) {
        CVB_LOG_DEBUG(CMSCBB_ERR_CONTEXT_INVALID_PARAM, CVB_NULL);
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    if (nameExpect->names.num != nameActual->names.num) {
        CVB_LOG_DEBUG(CMSCBB_ERR_UNDEFINED, CVB_NULL);
        return (CMSCBB_ERR_UNDEFINED);
    }

    for (iter = 0; iter != (CVB_INT)nameActual->names.num; ++iter) {
        const CmscbbX509AttrBundle *attrExp = &(nameExpect->names.data[iter]);
        const CmscbbX509AttrBundle *attrActual = &(nameActual->names.data[iter]);

        if (CMSCBB_COMPARE_ASN_OID(&(attrExp->attrs.data[0].id), &(attrActual->attrs.data[0].id)) != 0) {
            CVB_LOG_DEBUG(CMSCBB_ERR_UNDEFINED, CVB_NULL);
            return (CMSCBB_ERR_UNDEFINED);
        }
        if (CmscbbCompareAsnOcts(&(attrExp->attrs.data[0].value), &(attrActual->attrs.data[0].value)) != 0) {
            CVB_LOG_DEBUG(CMSCBB_ERR_UNDEFINED, CVB_NULL);
            return (CMSCBB_ERR_UNDEFINED);
        }
    }

    return CVB_SUCCESS;
}

#if defined(CVB_DEBUG) || CMSCBB_SUPPORT_CRL_COMPARE
#define CVB_MAX_ATTR_LEN 128

CVB_STATIC CVB_UINT32 InternalGetMaxAttrLen(CVB_UINT32 attrCap, CVB_UINT32 prefix, CVB_UINT32 attrLen)
{
    CVB_UINT32 ret = attrCap - prefix;
    if (attrLen >= ret) {
        CVB_LOG_WARNING(CMSCBB_ERR_SYS_STR, "attribute name too long, which is invalid.");
        return ret - 1;
    }

    return attrLen;
}

/*
 * Prototype    : InternalMakeReadableAttr
 * Params
 *   [IN] readableAttr: readable attribute
 *   [IN] attr: X509 certificate attribute
 *   [IN] prefix: prefix of pszReadableAttr
 * Return Value : CVB_VOID
 *   Date              Author     Modification
 *   2015/11/10 18:12  tangzonglei  Create
 */
CVB_STATIC CVB_VOID InternalMakeReadableAttr(CVB_CHAR *readableAttr, CVB_UINT32 attrCap,
    const CmscbbX509AttrEntry *attr, const CVB_CHAR *prefix)
{
    CMSCBB_ERROR_CODE ret;
    CVB_UINT32 prefixLen = CmscbbStrlen(prefix);
    CVB_UINT32 attrLen;

    ret = (CMSCBB_ERROR_CODE)strncpy_s(readableAttr, (CVB_SIZE_T)attrCap, prefix, prefixLen);
    if (CVB_FAILED(ret)) {
        return;
    }
    attrLen = InternalGetMaxAttrLen(attrCap, prefixLen, attr->value.len);
    ret = (CMSCBB_ERROR_CODE)memcpy_s(readableAttr + prefixLen, (CVB_SIZE_T)attrCap - prefixLen,
        attr->value.octs, attrLen);
    if (CVB_FAILED(ret)) {
        return;
    }

    readableAttr[prefixLen + attrLen] = 0;
}

/*
 * Prototype    : InternalConvertFromNameAttribute
 * Description  : Convert name attribute to readable string
 * Params
 *   [IN] attr: X509 certificate attribute
 *   [IN] pszReadableAttr: readable attribute
 * Return Value : CMSCBB_ERROR_CODE
 *   Date              Author     Modification
 *   2015/11/10 19:05  tangzonglei  Create
 */
CVB_STATIC CMSCBB_ERROR_CODE InternalConvertFromNameAttribute(const CmscbbX509AttrEntry *attr,
    CVB_CHAR *readableAttr, CVB_UINT32 attrCap)
{
    CmscbbAoids attrId;

    attrId = CmscbbFindAoid(&(attr->id));
    if (attrId == AOID_UNKNOWN) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CERT_DECODE, CVB_NULL);
        return (CMSCBB_ERR_PKI_CERT_DECODE);
    }
    if (attrId == AOID_AT_COMMONNAME) {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"CN=");
    } else if (attrId == AOID_AT_LOCALITYNAME) {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"L=");
    } else if (attrId == AOID_AT_STATEORPROVINCENAME) {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"S=");
    } else if (attrId == AOID_AT_ORGANIZATIONNAME) {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"O=");
    } else if (attrId == AOID_AT_ORGANIZATIONALUNITNAME) {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"OU=");
    } else if (attrId == AOID_AT_COUNTRYNAME) {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"C=");
    } else if (attrId == AOID_PKCS9_AT_EMAILADDRESS) {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"E=");
    } else {
        InternalMakeReadableAttr(readableAttr, attrCap, attr, (const CVB_CHAR *)"X=");
    }

    return CVB_SUCCESS;
}

#define CVB_MAX_X509_NAME_LEN MAX_ISSUER_NAME_LENGTH
CMSCBB_ERROR_CODE CmscbbConvertFromX509Name(const CmscbbX509Name *name, CVB_CHAR **readableName,
    CVB_UINT32 *nameLen)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT iter;
    CVB_CHAR *szName = CVB_NULL;
    CVB_BOOL legalFlag = (name == NULL) || (readableName == NULL) || (nameLen == NULL);

    if (legalFlag) {
        return CMSCBB_ERR_UNDEFINED;
    }
    ret = CmscbbMallocWith0((CVB_VOID **)&szName, CVB_MAX_X509_NAME_LEN);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    for (iter = 0; iter < (CVB_INT)name->names.num; ++iter) {
        CVB_INT iterAttr;
        const CmscbbX509AttrBundle *attrs = &(name->names.data[iter]);

        for (iterAttr = 0; iterAttr < (CVB_INT)attrs->attrs.num; ++iterAttr) {
            const CmscbbX509AttrEntry *attr = &(attrs->attrs.data[iterAttr]);
            CVB_CHAR szAttr[CVB_MAX_ATTR_LEN] = {0};

            ret = InternalConvertFromNameAttribute(attr, szAttr, CVB_MAX_ATTR_LEN);
            CVB_GOTO_ERR_IF_FAIL(ret);

            if (CmscbbStrlen(szName) + CmscbbStrlen(szAttr) + 1 >= CVB_MAX_X509_NAME_LEN) {
                break;
            }

            if (szName[0] != 0) {
                ret = (CMSCBB_ERROR_CODE)strcat_s(szName, CVB_MAX_X509_NAME_LEN, ",");
                CVB_GOTO_ERR_IF_FAIL(ret);
            }
            ret = (CMSCBB_ERROR_CODE)strcat_s(szName, CVB_MAX_X509_NAME_LEN, szAttr);
            CVB_GOTO_ERR_IF_FAIL(ret);
        }
    }

    *readableName = szName;
    *nameLen = CmscbbStrlen(szName);
    return ret;

CVB_ERR:
    CmscbbFreeWithNullCheck(szName);
    return ret;
}
#endif

CMSCBB_ERROR_CODE CmscbbCryptoVerifyQuick(const CmscbbAsnOcts* srcInfo, const CmscbbAsnOcts* sigInfo,
    CmscbbKeyAndAlgInfo* verifyInfo, CVB_INT *result)
{
    CMSCBB_ERROR_CODE ret;
    CMSCBB_CRYPTO_VRF_CTX vrfCtx = CVB_NULL;
    ret = CmscbbCryptoVerifyCreateCtx(&vrfCtx);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

#if CMSCBB_SUPPORT_RSAPSS
    ret = CmscbbCryptoVerifyInit(vrfCtx, verifyInfo);
#else
    ret = CmscbbCryptoVerifyInit(vrfCtx, verifyInfo->e, verifyInfo->n, verifyInfo->hashAlg);
#endif
    if (CVB_FAILED(ret)) {
        CmscbbCryptoVerifyDestroyCtx(vrfCtx);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    ret = CmscbbCryptoVerifyUpdate(vrfCtx, srcInfo->octs, srcInfo->len);
    if (CVB_FAILED(ret)) {
        CmscbbCryptoVerifyDestroyCtx(vrfCtx);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    ret = CmscbbCryptoVerifyFinal(vrfCtx, sigInfo->octs, sigInfo->len, result);
    if (CVB_FAILED(ret)) {
        CmscbbCryptoVerifyDestroyCtx(vrfCtx);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    CmscbbCryptoVerifyDestroyCtx(vrfCtx);
    return ret;
}

CVB_STATIC CMSCBB_ERROR_CODE InternalCheckParmX509PKeyVerify(const CmscbbAsnOcts* src, const CmscbbAsnOcts* sig,
    const CmscbbX509PubkeyInfo *pubKey, const CmscbbX509AlgoPssIdentifier* alg, const CVB_BOOL *result)
{
    CVB_BOOL legalFlag = (src == CVB_NULL) || (sig == CVB_NULL) || (pubKey == CVB_NULL) || (result == CVB_NULL) ||
        (alg == NULL);
    if (legalFlag) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbX509PubKeyVerify(const CmscbbAsnOcts* srcInfo, const CmscbbAsnOcts* sigInfo,
    const CmscbbX509PubkeyInfo* pubKey, const CmscbbX509AlgoPssIdentifier* alg, CVB_BOOL *result)
{
    CMSCBB_ERROR_CODE ret;
    CVB_INT res = CVB_FALSE;
    CmscbbKeyAndAlgInfo *verifyInfo = NULL;
    ret = CmscbbCreateVerifyInfo(&verifyInfo);
    if (CVB_FAILED(ret)) {
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    ret = InternalCheckParmX509PKeyVerify(srcInfo, sigInfo, pubKey, alg, result);
    if (CVB_FAILED(ret)) {
        CmscbbDestoryVerifyInfo(verifyInfo);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }

    ret = CmscbbGetCertVerifyInfo(pubKey, alg, sigInfo, verifyInfo);
    if (CVB_FAILED(ret)) {
        CmscbbDestoryVerifyInfo(verifyInfo);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    ret = CmscbbCryptoVerifyQuick(srcInfo, sigInfo, verifyInfo, &res);
    if (CVB_FAILED(ret)) {
        CmscbbDestoryVerifyInfo(verifyInfo);
        CVB_LOG_DEBUG(ret, CVB_NULL);
        return ret;
    }
    *result = (CVB_BOOL)res;
    CmscbbDestoryVerifyInfo(verifyInfo);
    return ret;
}

/**
 * Prototype    : CmscbbCheckAlgoPara
 * Description  : check if the PSS parameter is valid.
 * Params
 *   [IN] subjCert: X509 subject certificate
 * Return Value : CVB_STATIC CMSCBB_ERROR_CODE
 */
CMSCBB_ERROR_CODE CmscbbCheckAlgoPara(const CmscbbX509AlgoPssIdentifier* keyInfo,
    const CmscbbX509AlgoPssIdentifier* sigAlgo)
{
    CmscbbAoids encAglo = CmscbbGetEncAoidFromSign(&(keyInfo->algorithm));
    if (encAglo != AOID_RSASSAPSS) {
        return CVB_SUCCESS;
    }
    if (keyInfo->rawSigned.len == 0 && keyInfo->rawSigned.octs == NULL) {
        return CVB_SUCCESS;
    }
    if (CMSCBB_COMPARE_ASN_OID(&(keyInfo->pssPara.hashAlgorithm.algorithm),
        &(sigAlgo->pssPara.hashAlgorithm.algorithm)) != 0) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL, "hashAlgo not equal!");
        return CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL;
    }
    if (CMSCBB_COMPARE_ASN_OID(&(keyInfo->pssPara.maskGenAlgorithm.maskAlgo.algorithm),
        &(sigAlgo->pssPara.maskGenAlgorithm.maskAlgo.algorithm)) != 0) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL, "maskGenAlgorithm not equal!");
        return CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL;
    }
    if (keyInfo->pssPara.saltLength > sigAlgo->pssPara.saltLength) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL, "saltLength not equal!");
        return CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL;
    }
    if (keyInfo->pssPara.trailerField != sigAlgo->pssPara.trailerField) {
        CVB_LOG_DEBUG(CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL, "trailerField not equal!");
        return CMSCBB_ERR_PKI_CMS_PSS_PARAMETER_NOT_EQUAL;
    }
    return CVB_SUCCESS;
}
